package com.dxhackday.ppicker;

import com.dxhackday.ppicker.model.Photo;
import com.dxhackday.ppicker.model.PhotoManager;
import javafx.animation.*;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyEvent;
import javafx.stage.DirectoryChooser;
import javafx.stage.Screen;
import javafx.util.Duration;

import java.io.File;
import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;
import java.util.logging.Logger;

/**
 * @author Sergey Lyadzhin
 * @since 24.11.12 13:00
 */
public class PhotoPlaybackController implements Initializable {
    private final Logger log = Logger.getLogger(PhotoPlaybackController.class.getName());

    public static final Duration ONE_SECOND = Duration.seconds(1);

    @FXML ImageView imageView;
    @FXML Label totalCounterLabel;
    @FXML Label goodCounterLabel;
    @FXML ProgressIndicator progressIndicator;
    @FXML Node controls;

    private Timeline timeline;
    private final PhotoManager photoManager = ApplicationContext.getInstance().getPhotoManager();

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        replayFromStart();
    }

    private void replay() {
        if (timeline != null) {
            timeline.stop();
            timeline = null;
        }

        timeline = new Timeline();
        timeline.setCycleCount(photoManager.getCurrentList().size() - photoManager.getCurrentIndex() + 2);
        timeline.getKeyFrames().add(
                new KeyFrame(Duration.millis(ApplicationContext.getInstance().getPictureDelayMillis()), new EventHandler<ActionEvent>() {
                    @Override
                    public void handle(ActionEvent actionEvent) {
                        gotoNextPhoto();
                    }
                }));
        timeline.play();

        gotoNextPhoto();
    }

    public Photo gotoNextPhoto() {
        PhotoManager manager = photoManager;
        final Photo photo;
        if (manager.hasNext()) {
            photo = manager.next();
            if (photo.getFxImage().progressProperty().intValue() != 1) {
                progressIndicator.setVisible(true);
                timeline.pause();
            }
            photo.getFxImage().progressProperty().addListener(new ChangeListener<Number>() {
                @Override
                public void changed(ObservableValue<? extends Number> observableValue, Number oldValue, Number newValue) {
                    log.info("Changed for " + photo.getFile().getName() + " to " + newValue);
                    if (newValue.intValue() == 1) {
                        progressIndicator.setVisible(false);
                        timeline.play();
                    } else {
                        timeline.pause();
                        progressIndicator.setVisible(true);
                    }
                }
            });
            if (photo.getFxImage().progressProperty().intValue() == 1) {
                progressIndicator.setVisible(false);
                timeline.play();
            }
            imageView.setImage(photo.getFxImage());
        } else {
            photo = null;
            imageView.setImage(null);
            controls.setVisible(true);
            timeline.stop();
            timeline = null;
        }

        totalCounterLabel.setText((manager.getCurrentIndex() + 1) + "/" + manager.getCurrentList().size());
        goodCounterLabel.setText(Integer.toString(manager.getList(ApplicationContext.GOOD_LIST).size()));

        return photo;
    }

    public void handlePlayAgain(ActionEvent actionEvent) {
        controls.setVisible(false);
        replayFromStart();
    }

    public void handleUpload(ActionEvent actionEvent) {
        DirectoryChooser chooser = new DirectoryChooser();
        chooser.setTitle("Choose the folder to upload your best photos");
        String defaultGoodDir = ApplicationContext.getInstance().getConfig().getGoodDir();
        if (defaultGoodDir != null) {
            File defaultInputDirFile = new File(defaultGoodDir);
            if (defaultInputDirFile.exists())
                chooser.setInitialDirectory(defaultInputDirFile);
        }
        File goodDirFile = chooser.showDialog(imageView.getScene().getWindow());
        if (goodDirFile == null) {
            return;
        }
        List<Photo> photoList = photoManager.getList(ApplicationContext.GOOD_LIST);
        if (photoList.isEmpty())
            return;
        PhotoManager.savePictureList(goodDirFile.toString(), photoList);

    }

    public void handleExit(ActionEvent actionEvent) {
        Platform.exit();
    }

    public void handleKeyPressed(KeyEvent keyEvent) {
        switch (keyEvent.getCode()) {
            case SPACE:
                markPhoto(ApplicationContext.GOOD_LIST, createFlyingOutTransition());
                break;
            case DELETE:
                markPhoto(ApplicationContext.BAD_LIST, createCrashingTransition());
                break;
            case ENTER:
                pauseResumePlay();
                break;
            case BACK_SPACE:
                replayFromStart();
                break;
            case DOWN:
                changeSpeed(250);
                break;
            case UP:
                changeSpeed(-250);
                break;
            default:
                break;
        }
    }

    private void changeSpeed(int i) {
        int current = ApplicationContext.getInstance().getPictureDelayMillis();
        ApplicationContext.getInstance().setPictureDelayMillis(Math.max(100, current + i));
        replay();
    }

    private void replayFromStart() {
        photoManager.setCurrentList(ApplicationContext.BASE_LIST);
        replay();
    }

    private void pauseResumePlay() {
        if (timeline == null)
            return;

        if (timeline.getStatus() == Animation.Status.RUNNING) {
            timeline.pause();
        } else if (timeline.getStatus() == Animation.Status.PAUSED) {
            timeline.play();
        }
    }

    private void markPhoto(final String listName, Transition transition) {
        if (timeline == null)
            return;

        log.info(String.format("Marking photo as %s: %s ", listName, photoManager.getCurrentPhoto().getFile()));

        timeline.pause();
        transition.setOnFinished(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                resetImageView();

                photoManager.moveCurrentPhotoToList(listName);
                goodCounterLabel.setText(Integer.toString(photoManager.getList(ApplicationContext.GOOD_LIST).size()));
                timeline.play();
            }
        });
        transition.playFromStart();
    }

    private void resetImageView() {
        imageView.setImage(null);
        imageView.setOpacity(1.0);
        imageView.setScaleX(1.0);
        imageView.setScaleY(1.0);
        imageView.setRotate(0.0);
        imageView.setTranslateX(0);
        imageView.setTranslateY(0);
    }

    private ParallelTransition createFlyingOutTransition() {
        Duration animationTime = ONE_SECOND;
        return new ParallelTransition(imageView,
                            TranslateTransitionBuilder.create()
                                    .duration(animationTime)
                                    .toX(Screen.getPrimary().getVisualBounds().getWidth() / 2)
                                    .toY(-Screen.getPrimary().getVisualBounds().getHeight() / 2)
                                    .build(),
                            RotateTransitionBuilder.create()
                                    .duration(animationTime)
                                    .toAngle(360)
                                    .build(),
                            ScaleTransitionBuilder.create()
                                    .duration(animationTime)
                                    .toX(0.01)
                                    .toY(0.01)
                                    .build(),
                            FadeTransitionBuilder.create()
                                    .fromValue(1.0)
                                    .toValue(0.0)
                                    .duration(animationTime)
                                    .build()
                    );
    }

    private ParallelTransition createCrashingTransition() {
        Duration animationTime = ONE_SECOND;
        return new ParallelTransition(imageView,
                ScaleTransitionBuilder.create()
                        .duration(animationTime)
                        .toX(0.01)
                        .toY(0.01)
                        .build(),
                FadeTransitionBuilder.create()
                        .fromValue(1.0)
                        .toValue(0.0)
                        .duration(animationTime)
                        .build()
        );
    }
}
